package com.itheima.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.dao.DishDao;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.entity.dto.DishDto;
import com.itheima.reggie.entity.dto.PageQueryDto;
import com.itheima.reggie.service.CategoryService;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import com.itheima.reggie.web.exception.BusinessException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 菜品业务层实现类
 *
 * @Author Vsunks.v
 * @Date 2022/8/14 10:36
 * @Blog blog.sunxiaowei.net/996.mba
 * @Description: 菜品业务层实现类
 */
@Service
@Transactional
public class DishServiceImpl extends ServiceImpl<DishDao, Dish> implements DishService {


    @Autowired
    DishDao dishDao;

    @Autowired
    DishFlavorService dishFlavorService;


    @Autowired
    CategoryService categoryService;

    /**
     * 新增菜品，并检查名称是否重复。
     *
     * @param dish 要新增的菜品
     * @return 是否新增成功
     */
    @Override
    public boolean saveWithNameCheck(Dish dish) {
        // 检查名称是否重复
        checkDuplicateName(dish);

        // 不重复，添加，并返回添加是否成功
        return this.save(dish);
    }


    @Override
    @Transactional(rollbackFor = Exception.class) // 让所有异常都回滚（编译时异<SQL>常默认不会滚）
    public boolean saveWithNameCheckAndSaveFlavors(DishDto dishDto) {
        // 检查名称是否重复
        checkDuplicateName(dishDto);


        // 保存菜品基本信息，把生成的id设置进DishDto
        boolean result = this.save(dishDto);

        // 获取口味信息
        List<DishFlavor> flavors = dishDto.getFlavors();

        // 没有口味信息，不需要保存口味信息
        if (flavors.size() <= 0) {
            return result;
        }

        // 设置菜品口味中菜品的 id
        for (DishFlavor flavor : flavors) {
            flavor.setDishId(dishDto.getId());
        }


        if (result) {
            // 保存口味信息
            return dishFlavorService.saveBatch(flavors);
        }
        return false;
    }


    /**
     * 分页查询菜品，并关联查询菜品分类名称
     *
     * @param pageQueryDto 分页条件对象
     * @return
     */
    @Override
    public Page<DishDto> page(PageQueryDto pageQueryDto) {
        // 封装分页对象
        Page<Dish> page = new Page<>(pageQueryDto.getPage(), pageQueryDto.getPageSize());


        // 封装查询条件
        LambdaQueryWrapper<Dish> lqw = Wrappers.lambdaQuery(Dish.class);
        String name = pageQueryDto.getName();
        lqw.like(StringUtils.isNotBlank(name), Dish::getName, name)
                .orderByAsc(Dish::getSort)
                .orderByDesc(Dish::getUpdateTime);


        // 查询
        dishDao.selectPage(page, lqw);

        // dish  -> dishDto

        /*
            page<Dish>
                - 当前页
                - 总页数
                - 页面大小
                -....
                -  records: List<Dish>



            paeg<DishDto>
                - 当前页
                - 总页数
                - 页面大小
                -....
                -  records: List<DishDto>

         */

        Page<DishDto> dtoPage = new Page<>();

        // 复制相同数据，
        // BeanUtils.copyProperties，复制属性，属性名一致就复制
        // 忽略records，虽然属性名一致，但是类型不一样
        BeanUtils.copyProperties(page, dtoPage, "records");


        streamMethod(page, dtoPage);

        // 响应
        return dtoPage;
    }

    /**
     * 根据id查询菜品（含口味信息）
     *
     * @param id id
     * @return 含口味信息的菜品对象
     */
    @Override
    public DishDto getWithFlavorsById(Long id) {

        //  根据id查询菜品基本信息
        Dish dish = this.getById(id);

        if (dish == null) {
            return null;
        }

        DishDto dishDto = new DishDto();

        // 复制数据
        BeanUtils.copyProperties(dish, dishDto);

        // 根据菜品id查询口味信息
        LambdaQueryWrapper<DishFlavor> lqw = Wrappers.lambdaQuery(DishFlavor.class);
        lqw.eq(DishFlavor::getDishId, dish.getId());
        List<DishFlavor> flavors = dishFlavorService.list(lqw);

        // 组织数据返回
        dishDto.setFlavors(flavors);
        return dishDto;
    }

    /**
     * 修改菜品，包含修改菜品口味
     *
     * @param dishDto 新的含有口味的菜品对象
     * @return 是否修改成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWithFlavorsById(DishDto dishDto) {

        // 修改菜品基本信息
        boolean result = this.updateById(dishDto);

        // 如果菜品基本信息修改成功，就继续修改口味
        if (result) {
            // 修改口味信息
            // 先删除所有
            LambdaQueryWrapper<DishFlavor> lqw = Wrappers.lambdaQuery(DishFlavor.class);
            lqw.eq(DishFlavor::getDishId, dishDto.getId());
            dishFlavorService.remove(lqw);

            // 确认口味中含有菜品id
            List<DishFlavor> flavors = dishDto.getFlavors();
            for (DishFlavor flavor : flavors) {
                flavor.setDishId(dishDto.getId());
            }

            // 再添加新的
            dishFlavorService.saveBatch(flavors);

            // 修改成功
            return true;
        }
        // 返回结果
        return false;
    }

    /**
     * 批量启售停售
     *
     * @param status 目标状态，0表示禁售 1表示起售
     * @param ids    要禁售/起售的菜品id
     * @return 是否成功
     */
    @Override
    public boolean switchStatus(Integer status, Long[] ids) {

        // 修改 ，并返回修改结果
        return dishDao.updateStatusByIds(status, ids) > 0;

    }

    /**
     * 使用普通for循环遍历并组织数据
     *
     * @param page
     * @param dtoPage
     */
    private void forMethod(Page<Dish> page, Page<DishDto> dtoPage) {
        // 手动处理records
        List<Dish> records = page.getRecords();
        ArrayList<DishDto> dtoRecords = new ArrayList<>();


        // 为了降低mysql服务器压力，统一查询一次所有的菜品分类信息
        List<Category> categories = categoryService.listByType(1);


        // 遍历得到每一个菜品
        for (Dish dish : records) {
            // 组装成一个DishDto（复制已有所有属性的值，再添加一个categoryName值）
            DishDto dishDto = new DishDto();

            //  复制已有数据
            BeanUtils.copyProperties(dish, dishDto);

            // 添加一个categoryName值
            Long categoryId = dish.getCategoryId();

            // 为了降低mysql服务器压力，统一查询一次所有的菜品分类信息
            // 遍历该分类集合，根据分类id查找对应的分类名称
            for (Category category : categories) {
                if (category.getId().equals(categoryId)) {
                    dishDto.setCategoryName(category.getName());
                    // 当前菜品DTO组织完成，开始组织下个DTO
                    break;
                }
            }
            // 添加进目标集合records
            dtoRecords.add(dishDto);
        }

        // 把dtoRecords设置进dtoPage
        dtoPage.setRecords(dtoRecords);
    }

    /**
     * 使用stream遍历并组织数据
     *
     * @param page
     * @param dtoPage
     */
    private void streamMethod(Page<Dish> page, Page<DishDto> dtoPage) {
        // 手动处理records
        List<Dish> records = page.getRecords();


        // 为了降低mysql服务器压力，统一查询一次所有的菜品分类信息
        List<Category> categories = categoryService.listByType(1);

        List<DishDto> dtoRecords = records.stream().map(dish -> {
            // 组装成一个DishDto（复制已有所有属性的值，再添加一个categoryName值）
            DishDto dishDto = new DishDto();

            //  复制已有数据
            BeanUtils.copyProperties(dish, dishDto);

            // 添加一个categoryName值

            Long categoryId = dish.getCategoryId();


            // 为了降低mysql服务器压力，统一查询一次所有的菜品分类信息
            // 遍历该分类集合，根据分类id查找对应的分类名称

            Category category =
                    categories.stream().filter(cat -> cat.getId().equals(categoryId)).findFirst().get();

            String name = category.getName();
            dishDto.setCategoryName(name);

            return dishDto;

        }).collect(Collectors.toList());

        // 把dtoRecords设置进dtoPage
        dtoPage.setRecords(dtoRecords);

    }

    /**
     * 检查名称是否重复，兼容更新和添加，包括修改的时候不修改名称
     *
     * @param dish
     */
    private void checkDuplicateName(Dish dish) {
        // 根据用户名查询
        LambdaQueryWrapper<Dish> lqw = Wrappers.lambdaQuery(Dish.class);
        String name = dish.getName();
        // lqw.eq(StringUtils.isNotBlank(name), Employee::getUsername, name);
        lqw.eq(Dish::getName, name);
        Dish dishInDb = dishDao.selectOne(lqw);


        // 判断对象是否为空，不为空则表示名称重复了，需要抛异常
        if (dishInDb != null) {
            // 避免查询到自己之后产生的bug
            if (!dishInDb.getId().equals(dish.getId())) {
                throw new BusinessException("分类名称：" + name + "重复");
            }
        }
    }
}
